package graphics.main;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.atomic.AtomicReference;

import graphics.system.GLTools;
import graphics.system.Renderer;

import main.Config;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;

import sound.main.SoundManager;
import tools.Point;
import tools.Profiler;
public class Window {
	private Renderer renderer;
	private int maxfps;
	private long lastFPS;
	private long fps;
	public Point res;
	private String title;
	private boolean closeRequested;
	private Frame frame;
	protected boolean resize;
	private Canvas canvas;
	private AtomicReference<Dimension> newCanvasSize = new AtomicReference<Dimension>();
	private long lastframetime;
	public Window() {
		SoundManager.initialize();
		SoundManager.setVolume(Config.VOLUME);
		resize = false;
		maxfps = 1;
		res = Config.RESOLUTION;
		frame = new Frame("rohXel");
		frame.setLayout(new BorderLayout());
		canvas = new Canvas();
		canvas.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				resize = true;
				newCanvasSize.set(canvas.getSize());
			}
		});
		frame.addWindowFocusListener(new WindowAdapter() {
			@Override
			public void windowGainedFocus(WindowEvent e) {
				canvas.requestFocusInWindow();
			}
		});
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				closeRequested = true;
			}
		});
		frame.add(canvas, BorderLayout.CENTER);
		try {
			Display.setParent(canvas);
			frame.setPreferredSize(new Dimension((int) Config.RESOLUTION.x, (int) Config.RESOLUTION.y));
			frame.setMinimumSize(new Dimension(200, 200));
			frame.pack();
			frame.setVisible(true);
			Display.create(new PixelFormat(8, 0, 1));
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}
		reshape();
		lastFPS = (int) getTime();
		fps = 0;
	}
	public void reshape() {
		GLTools.reshape2D(Config.RESOLUTION.x, Config.RESOLUTION.y);
	}
	/**
	 * Mainloop
	 */
	public void startMainloop() {
		while (!closeRequested) {
			this.update();
			Display.update();
			if (resize) {
				renderer.resize(canvas.getWidth(), canvas.getHeight());
				canvas.setSize(canvas.getWidth(), canvas.getHeight());
				resize = false;
			}
			// Display.sync(maxfps);
			sync();
			updateFPS();
			try {
				Profiler.startGraphics();
				Display.swapBuffers();
				Profiler.stopGraphics();
			} catch (LWJGLException e) {
				e.printStackTrace();
			}
		}
		Display.destroy();
		frame.dispose();
		SoundManager.end();
	}
	/**
	 * inside mainloop
	 */
	public void update() {
		if (renderer != null) {
			renderer.handle();
		}
	}
	public Renderer getRenderer() {
		return renderer;
	}
	/**
	 * Sets the renderer and calls its initialize function
	 * 
	 * @param renderer
	 *            Renderer which will render all objects
	 */
	public void setRenderer(Renderer renderer) {
		this.renderer = renderer;
	}
	public int getMaxfps() {
		return maxfps;
	}
	public void setMaxfps(int fps) {
		this.maxfps = fps;
	}
	/**
	 * Get the accurate system time
	 * 
	 * @return The system time in milliseconds
	 */
	private long getTime() {
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
	/**
	 * Calculate the FPS and set it in the title bar
	 */
	private void updateFPS() {
		if (getTime() - lastFPS > 1000) {
			frame.setTitle(title + " fps: " + fps);
			renderer.setCurrentFps((int) fps);
			setMaxfps(renderer.getWantedFps());
			fps = 0;
			lastFPS = getTime();
		}
		fps++;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public void sync() {
		long framelength = System.currentTimeMillis() - lastframetime;
		long minflength = 1000 / maxfps;
		if (framelength < minflength) {
			try {
				Thread.sleep(minflength - framelength);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		lastframetime = System.currentTimeMillis();
	}
	public void cleanUp() {
	}
}
